package com.yirui.serialcomm.core;

import java.io.*;
import java.util.Iterator;
import java.util.zip.GZIPInputStream;

/**
 * 分析输入流GZIP头，根据Gzip块遍历获取解压后数据流
 * @author Dujie
 * @create 2025/8/8
 * @since 1.0.0
 */
public class GzipChunkIterator implements Iterator<byte[]>, Closeable {

    private final PushbackInputStream pushbackIn;
    private boolean hasNextChunk = true;
    private byte[] nextHeader; // 缓存下一个 GZIP 头 (0x1F 0x8B)

    public GzipChunkIterator(InputStream inputStream) {
        this.pushbackIn = new PushbackInputStream(new BufferedInputStream(inputStream), 2);

    }

    @Override
    public boolean hasNext() {
        this.nextHeader = findNextGzipHeader();
        return hasNextChunk && (nextHeader != null);
    }

    @Override
    public byte[] next() {

        try (// 构造当前 GZIP 块的输入流（头 + 剩余数据）
             InputStream chunkIn = new SequenceInputStream(
                     new ByteArrayInputStream(nextHeader),
                     extractBody(pushbackIn)
             );
             // 解压当前块（惰性读取，不预读后续数据）
             ByteArrayOutputStream byteOut = new ByteArrayOutputStream();
             GZIPInputStream gzipIn = new GZIPInputStream(chunkIn) ){


            byte[] buffer = new byte[1024];
            int len;
            while ((len = gzipIn.read(buffer)) != -1) {
                byteOut.write(buffer, 0, len);
            }
            return byteOut.toByteArray();

        } catch (IOException e) {
            hasNextChunk = false;
            throw new RuntimeException("Failed to read GZIP chunk", e);
        }
    }

    /**
     * 对流按照每个zip包进行分批处理
     * @param pushbackIn
     * @return
     * @throws IOException
     */
    private InputStream extractBody(PushbackInputStream pushbackIn) throws IOException {
        try(ByteArrayOutputStream byteOut = new ByteArrayOutputStream()) {
            //byteOut.write(nextHeader);
            int b1, b2;
            while (pushbackIn.available()>0 && (b1 = pushbackIn.read()) != -1) {

                if (b1 == 0x1F) {
                    //出现断包如：1f8b080000000000000033e0646264606149040200ad2769bf0b0000001f
                    if(pushbackIn.available() == 0 && byteOut.size()>0){
                        pushbackIn.unread(b1);
                        return new ByteArrayInputStream(byteOut.toByteArray());
                    }
                    b2 = pushbackIn.read();
                    if (b2 == 0x8B) {
                        //回退2个字节
                        pushbackIn.unread(b2);
                        pushbackIn.unread(b1);
                        if(byteOut.size()>0) {
                            //System.out.println(HexUtils.toHexString(byteOut.toByteArray()));
                            return new ByteArrayInputStream(byteOut.toByteArray());
                        }
                    } else if (b2 != -1) {
                        // 不是 GZIP 头存储
                        byteOut.write(b2);
                    }
                } else {
                    byteOut.write(b1);
                }
            }
            if (byteOut.size() > 0) {
                //System.out.println(HexUtils.toHexString(byteOut.toByteArray()));
                return new ByteArrayInputStream(byteOut.toByteArray());
            } else {
                throw new IOException("Not GZIP chunk");
            }
        }
    }

    // 查找下一个 GZIP 头 (0x1F 0x8B)，不读取多余数据
    private byte[] findNextGzipHeader() {
        try {
            int b1, b2;
            while ((b1 = pushbackIn.read()) != -1) {
                if (b1 == 0x1F) {
                    b2 = pushbackIn.read();
                    if (b2 == 0x8B) {
                        hasNextChunk = true;
                        return new byte[]{(byte) b1, (byte) b2};
                    } else if (b2 != -1) {
                        // 不是 GZIP 头，回退第二个字节
                        pushbackIn.unread(b2);
                    }
                }
            }
            hasNextChunk = false;
            return null;
        } catch (IOException e) {
            hasNextChunk = false;
            return null;
        }
    }

    @Override
    public void close() throws IOException {
        pushbackIn.close();
    }
}
